import frontend.cehandler.CEHandler;
import frontend.lexer.Lexer;
import frontend.parser.Parser;
import frontend.parser.node.CompUnit;
import frontend.visitor.Visitor;
import utils.IOUtils;
import utils.cli.CommandLine;
import utils.cli.Options;
import utils.ioclasses.FileStringPrintWriter;

import java.io.Closeable;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Scanner;

public class Compiler implements Closeable {
    private final PrintWriter STDOUT = IOUtils.initWriter("_stdout");
//    private final PrintWriter STDERR = IOUtils.initWriter("_stderr");
    private final Scanner STDIN = IOUtils.intiScanner("_stdin");
    private final PrintWriter NULL_WRITER = IOUtils.initWriter("_null");
    private final Scanner INPUT;
    private final String INPUT_PATH;

    // 写入所有（传入子模块）
    private final FileStringPrintWriter LEXER_WRITER;
    private final FileStringPrintWriter PARSER_WRITER;
    private final FileStringPrintWriter VISITOR_WRITER;
    private final FileStringPrintWriter ERROR_WRITER;
    private final PrintWriter DEBUG_WRITER;

    private final ArrayList<String> sourceCodeList = new ArrayList<>();

    // 错误信息

    public Compiler(String inputPath,
                    String lexerOutputPath,
                    String parserOutputPath,
                    String visitorOutputPath,
                    String errorOutputPath,
                    boolean debug) throws IOException {

        this.INPUT = IOUtils.intiScanner(inputPath);
        this.INPUT_PATH = inputPath;
        this.LEXER_WRITER = IOUtils.initFileStringWriter(lexerOutputPath);
        this.PARSER_WRITER = IOUtils.initFileStringWriter(parserOutputPath);
        this.VISITOR_WRITER = IOUtils.initFileStringWriter(visitorOutputPath);
        this.ERROR_WRITER = IOUtils.initFileStringWriter(errorOutputPath);
        this.DEBUG_WRITER = debug ? STDOUT : NULL_WRITER;
    }

    /**
     * 核心方法
     */
    public void compile() {
        // pass 1
        CEHandler ceHandler = new CEHandler(INPUT_PATH, sourceCodeList, ERROR_WRITER, STDOUT);
        Lexer lexer = new Lexer(INPUT, sourceCodeList, LEXER_WRITER, DEBUG_WRITER, ceHandler);
        Parser parser = new Parser(lexer, PARSER_WRITER, DEBUG_WRITER, ceHandler);
        CompUnit compUnit = parser.analyse();

        // pass 2
        Visitor visitor = new Visitor(compUnit, VISITOR_WRITER, DEBUG_WRITER, ceHandler);
        visitor.analyse();
        ceHandler.printAll();
    }

    public String getLexerResult() {
        return LEXER_WRITER.toString();
    }
    public String getParserResult() {
        return PARSER_WRITER.toString();
    }
    public String getVisitorResult() {
        return VISITOR_WRITER.toString();
    }
    public String getErrorResult() {
        return ERROR_WRITER.toString();
    }


    @Override
    public void close() {
        this.STDOUT.close();
//        this.STDERR.close();
        this.STDIN.close();
        this.INPUT.close();
        this.LEXER_WRITER.close();
        this.PARSER_WRITER.close();
        this.VISITOR_WRITER.close();
        this.ERROR_WRITER.close();
        this.DEBUG_WRITER.close();
    }

    public static void main(String[] args) {
        // cmd init
        CommandLine commandLine = new CommandLine(new Options() {{
            addOption("d", "debug", true);
            addOption("h", "help", false);
            addOption("l", "lexer-output", true);
            addOption("p", "parser-output", true);
            addOption("v", "visitor-output", true);
            addOption("e", "error-output", true);
            addOption("cl", "lexer-compare-to", true);
            addOption("cp", "parser-compare-to", true);
            addOption("cv", "visitor-compare-to", true);
            addOption("ce", "parser-compare-to", true);
            addOption("C", "compare-only", false);
        }}, args);

        // config init
        Config config = new Config(commandLine);

        // debug
        if (config.DEBUG) {
            commandLine.printAll();
        }

        // help
        if (config.TODO == Config.Todo.HELP) {
            help();
        }

        // compile
        else if (config.TODO == Config.Todo.COMPILE) {

            try (Compiler compiler = new Compiler(
                    config.INPUT_PATH,
                    config.LEXER_OUTPUT_PATH,
                    config.PARSER_OUTPUT_PATH,
                    config.VISITOR_OUTPUT_PATH,
                    config.ERROR_OUTPUT_PATH,
                    config.DEBUG
            )) {

                // compile-main
                compiler.compile();

                // compare
                if (config.LEXER_COMPARE) {
                    IOUtils.compareStringFile(compiler.getLexerResult(), config.LEXER_COMPARE_PATH, String.format("LEXER<%s", config.INPUT_PATH));
                }
                if (config.PARSER_COMPARE) {
                    IOUtils.compareStringFile(compiler.getParserResult(), config.PARSER_COMPARE_PATH, String.format("PARSER<%s", config.INPUT_PATH));
                }
                if (config.VISITOR_COMPARE) {
                    IOUtils.compareStringFile(compiler.getVisitorResult(), config.VISITOR_COMPARE_PATH, String.format("VISITOR<%s", config.INPUT_PATH));
                }
                if (config.ERROR_COMPARE) {
                    IOUtils.compareStringFile(compiler.getErrorResult(), config.ERROR_COMPARE_PATH, String.format("ERROR<%s", config.INPUT_PATH));
                }

            } catch (IOException e) {
                e.printStackTrace();
            }

        }

        else if (config.TODO == Config.Todo.COMPARE_ONLY) {
            IOUtils.compareFiles(config.CO_PATH1, config.CO_PATH2);
        }


    }

    

    public static void help() {
        System.out.print("""

                用法：
                
                1.编译
                [<input-path>]
                [-l <output-path>/--lexer-output=<output-path>]
                [-p <output-path>/--parser-output=<output-path>]
                [-e <output-path>/--error-output=<output-path>]
                [-cl <compared-path>/--lexer-compare-to=<compared-path>]
                [-cp <compared-path>/--parser-compare-to=<compared-path>]
                [-ce <compared-path>/--error-compare-to=<compared-path>]
                如未给出<input-path>，默认输入路径为textfile.txt
                -l选项可用于输出词法分析结果
                -p选项可用于输出语法分析结果
                -e选项可用于输出错误处理结果
                如未给出-l、-p、-e，默认选项为-p，默认输出路径为output.txt
                <input-path>、<output-path>可为以下特殊值：_stdin（标准输入）、_stdout（标准输出）、_stderr（标准错误）、_null（空）
                -cl、-cp、-ce选项可用于将结果与<compared-path>文件内容对比
                
                2.仅对比文件
                (-C/--compare-only) <src-path> <compared-path>
                
                3.获取帮助
                -h/--help==<boolean>
                
                4.开启调试（用法1、2、3附加选项）
                [-d/--debug[=<boolean>]]
                开启调试后将输出以下内容到标准输出
                1）命令行参数解析结果
                2）错误处理详细信息
                3）符号表建立结果
                """);
    }

    static class Config {
        // default value

        // 所有情况均可带有的选项
        boolean DEBUG = false;

        // 情况枚举
        static enum Todo {
            COMPILE, COMPARE_ONLY, HELP;
        }
        Todo TODO = Todo.COMPILE;

        // COMPILE
        String INPUT_PATH = "_null";

        String LEXER_OUTPUT_PATH = "_null";
        boolean LEXER_COMPARE = false;
        String LEXER_COMPARE_PATH = null;

        String PARSER_OUTPUT_PATH = "_null";
        boolean PARSER_COMPARE = false;
        String PARSER_COMPARE_PATH = null;

        String VISITOR_OUTPUT_PATH = "_null";
        boolean VISITOR_COMPARE = false;
        String VISITOR_COMPARE_PATH = null;

        String ERROR_OUTPUT_PATH = "_null";
        boolean ERROR_COMPARE = false;
        String ERROR_COMPARE_PATH = null;

        // COMPARE_ONLY
        String CO_PATH1 = null;
        String CO_PATH2 = null;

        Config(CommandLine commandLine) {
            if (commandLine.has("d")) {
                this.DEBUG = commandLine.get2Boolean("d", false, true);
            }

            if (commandLine.has("h") && commandLine.get2Boolean("h", false, true)) {
                this.TODO = Config.Todo.HELP;
            }

            else if (commandLine.has("C") && commandLine.get2Boolean("C", false, true)) {
                this.TODO = Config.Todo.COMPARE_ONLY;
                this.CO_PATH1 = commandLine.get(0);
                this.CO_PATH2 = commandLine.get(1);
            }

            else {
                boolean isDefaultInput = true, isDefaultOutput = true;

                if (commandLine.has(0)) {
                    this.INPUT_PATH = commandLine.get(0);
                    isDefaultInput = false;
                }

                if (commandLine.hasOptionValue("l")) {
                    this.LEXER_OUTPUT_PATH = commandLine.get("l");
                    isDefaultOutput = false;
                }
                if (commandLine.hasOptionValue("p")) {
                    this.PARSER_OUTPUT_PATH = commandLine.get("p");
                    isDefaultOutput = false;
                }
                if (commandLine.hasOptionValue("v")) {
                    this.VISITOR_OUTPUT_PATH = commandLine.get("v");
                    isDefaultOutput = false;
                }
                if (commandLine.hasOptionValue("e")) {
                    this.ERROR_OUTPUT_PATH = commandLine.get("e");
                    isDefaultOutput = false;
                }

                if (commandLine.hasOptionValue("cl")) {
                    this.LEXER_COMPARE = true;
                    this.LEXER_COMPARE_PATH = commandLine.get("cl");
                    isDefaultOutput = false;
                }
                if (commandLine.hasOptionValue("cp")) {
                    this.PARSER_COMPARE = true;
                    this.PARSER_COMPARE_PATH = commandLine.get("cp");
                    isDefaultOutput = false;
                }
                if (commandLine.hasOptionValue("cv")) {
                    this.VISITOR_COMPARE = true;
                    this.VISITOR_COMPARE_PATH = commandLine.get("cv");
                    isDefaultOutput = false;
                }
                if (commandLine.hasOptionValue("ce")) {
                    this.ERROR_COMPARE = true;
                    this.ERROR_COMPARE_PATH = commandLine.get("ce");
                    isDefaultOutput = false;
                }

                /* Default case */
                // 无输入参数默认情况
                if (isDefaultInput) {
                    this.INPUT_PATH = "testfile.txt";
                }
                // 无输出参数默认情况
                if (isDefaultOutput) {
                    /* 词法分析 */
//                    this.LEXER_OUTPUT_PATH = "output.txt";
//                    this.PARSER_OUTPUT_PATH = "_null";
//                    this.VISITOR_OUTPUT_PATH = "_null.txt";
//                    this.ERROR_OUTPUT_PATH = "_null";

                    /* 语法分析 */
//                    this.LEXER_OUTPUT_PATH = "_null";
//                    this.PARSER_OUTPUT_PATH = "output.txt";
//                    this.VISITOR_OUTPUT_PATH = "_null.txt";
//                    this.ERROR_OUTPUT_PATH = "_null";

                    /* 语义分析与错误处理 */
                    this.LEXER_OUTPUT_PATH = "_null";
                    this.PARSER_OUTPUT_PATH = "_null";
                    this.ERROR_OUTPUT_PATH = "error.txt";
                    this.VISITOR_OUTPUT_PATH = "llvm_ir.txt";

                }
            }

        }
    }
}



